package com.xy.tool.report.excel;


import com.xy.tool.report.view.*;
import com.xy.tool.report.view.Cell;
import com.xy.tool.report.view.Row;
import com.xy.tool.report.view.Table;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.ss.util.CellRangeAddress;
import org.apache.poi.xssf.streaming.SXSSFCell;
import org.apache.poi.xssf.streaming.SXSSFRow;
import org.apache.poi.xssf.streaming.SXSSFSheet;
import org.apache.poi.xssf.streaming.SXSSFWorkbook;
import org.apache.poi.xssf.usermodel.XSSFClientAnchor;
import org.apache.poi.xssf.usermodel.XSSFDrawing;
import org.apache.poi.xssf.usermodel.XSSFSimpleShape;
import org.springframework.util.CollectionUtils;

import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;

/**
 * Created by zsp on 2018/12/7.
 */
public class ExcelTemplate {

    private final ConcurrentMap<String, CellStyle> cellStyleCache = new ConcurrentHashMap<>(128);

    public static SXSSFWorkbook createAlertMessage(String message) {
        SXSSFWorkbook workbook = new SXSSFWorkbook();
        SXSSFSheet sheet = workbook.createSheet();
        SXSSFRow row = sheet.createRow(0);
        SXSSFCell cell = row.createCell(0);
        cell.setCellType(CellType.STRING);
        cell.setCellValue("提示：" + message);
        CellStyle cellStyle = workbook.createCellStyle();
        Font font = workbook.createFont();
        font.setColor(IndexedColors.RED.getIndex());
        cellStyle.setFont(font);
        cell.setCellStyle(cellStyle);
        return workbook;
    }

    protected void createTable(Table table, SXSSFSheet sheet) {
        createTable(table, null, sheet);
    }

    protected void createTable(Table table, int[] columnWidths, SXSSFSheet sheet) {
        int offset = 0;
        List<Row> rowsList;
        List<SXSSFRow> rowViewList;
        rowsList = table.getHeaderList();
        if (!CollectionUtils.isEmpty(rowsList)) {
            rowViewList = new ArrayList<>();
            offset = createRow(table, offset, rowsList, false, sheet, rowViewList);
            merge(rowsList, rowViewList);
        }
        rowsList = table.getRowList();
        if (!CollectionUtils.isEmpty(rowsList)) {
            rowViewList = new ArrayList<>();
            offset = createRow(table, offset, rowsList, true, sheet, rowViewList);
            merge(rowsList, rowViewList);
        }

        rowsList = table.getFooterList();
        if (!CollectionUtils.isEmpty(rowsList)) {
            rowViewList = new ArrayList<>();
            createRow(table, offset, rowsList, false, sheet, rowViewList);
            merge(rowsList, rowViewList);
        }
        if (columnWidths != null && columnWidths.length > 0) {
            for (int i = 0, len = columnWidths.length; i < len; i++) {
                sheet.setColumnWidth(i, columnWidths[i]);
            }
        }
    }

    private void merge(List<Row> rowsList, List<SXSSFRow> rowViewList) {
        int rowSize = rowsList.size(), colSize, colSpan, rowSpan;
        List<Cell> cellList;
        SXSSFRow rowView;
        for (int rowIndex = 0; rowIndex < rowSize; rowIndex++) {
            cellList = rowsList.get(rowIndex).getCellList();
            colSize = cellList.size();
            rowView = rowViewList.get(rowIndex);
            for (int cellIndex = 0; cellIndex < colSize; cellIndex++) {
                Cell cell = cellList.get(cellIndex);
                colSpan = cell.getSpan();
                rowSpan = cell.getRowSpan();
                if (colSpan > 1 || rowSpan > 1) {
                    SXSSFCell cellView = rowView.getCell(cellIndex);
                    cellView.getSheet().addMergedRegion(new CellRangeAddress(rowIndex, rowIndex + (rowSpan - 1),
                            cellIndex, cellIndex + (colSpan - 1)));
                }
            }
        }
    }

    protected int createRow(Table table, int offset, List<Row> rowList, boolean isBody, SXSSFSheet sheet,
                            List<SXSSFRow> rowViewList) {
        int rowSize = rowList.size();
        int rowIndex, rowHeight;
        for (int i = 0; i < rowSize; i++) {
            Row row = rowList.get(i);
            rowIndex = offset + i;
            SXSSFRow rowView = sheet.createRow(rowIndex);
            if (rowViewList != null) {
                rowViewList.add(rowView);
            }
            rowHeight = row.getHeight();
            if (rowHeight > 0) {
                rowView.setHeight((short) rowHeight);
            }
            createCell(table, rowIndex, row, isBody, rowView);
        }
        return offset + rowSize;
    }

    protected void createCell(Table table, int rowIndex, Row row, boolean isBody, SXSSFRow rowView) {
        SXSSFSheet sheet = rowView.getSheet();
        List<Cell> cellList = row.getCellList();
        int colSize = cellList.size();
        for (int i = 0; i < colSize; i++) {
            Cell cell = cellList.get(i);
            SXSSFCell cellView = rowView.createCell(i);
            CellStyle cellStyle = createStyle(table, rowIndex, row, i, cell, isBody, sheet);
            cellView.setCellStyle(cellStyle);
            Object value = cell.getValue();
            if (value != null) {
                if (cell instanceof CrossCell) {
                    drawLine(sheet, i, rowIndex, i + 1, rowIndex + 1);
                }
                cellView.setCellType(CellType.STRING);
                if (value instanceof Number && ((Number) value).intValue() == 0) {
                    if (value instanceof BigDecimal) {
                        cellView.setCellValue(String.valueOf(value));
                    }
                    continue;
                }
                cellView.setCellValue(String.valueOf(value));
            }
        }
    }

    private CellStyle createStyle(Table table, int rowIndex, Row row, int colIndex, Cell cell,
                                  boolean isBody, SXSSFSheet sheet) {
        boolean striped = isBody && table.isStriped() && (colIndex % 2 == 0);
        int index = table.getStripeStartColIndex();
        if (index > 0) {
            striped = striped && colIndex >= index;
        }
        index = table.getStripeEndColIndex();
        if (index > 0) {
            striped = striped && colIndex <= index;
        }
        index = table.getStripeStartRowIndex();
        if (index > 0) {
            striped = striped && rowIndex >= index;
        }
        index = table.getStripeEndRowIndex();
        if (index > 0) {
            striped = striped && rowIndex <= index;
        }
        String key = isBody
                + "-" + striped
                + "-" + cell.getHorizontalAlignment()
                + "-" + cell.getVerticalAlignment()
                + "-" + cell.getStyle();
        boolean striped0 = striped;
        return cellStyleCache.computeIfAbsent(key, (key0) -> {
            CellStyle cellStyle = sheet.getWorkbook().createCellStyle();
            if (striped0) {
                cellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
                cellStyle.setFillPattern(FillPatternType.SOLID_FOREGROUND);
            }
            if (row.getBorder() > 0) {
                BorderStyle borderStyle = BorderStyle.THIN;
                cellStyle.setBorderBottom(borderStyle);
                cellStyle.setBorderLeft(borderStyle);
                cellStyle.setBorderRight(borderStyle);
                cellStyle.setBorderTop(borderStyle);
            }
            setHorizontalAlignment(cell, cellStyle);
            setVerticalAlignment(cell, cellStyle);
            cellStyle.setWrapText(true);
            Style style = cell.getStyle();
            if (!Objects.equals(Style.empty(), style)) {
                Font font = sheet.getWorkbook().createFont();
                font.setFontHeightInPoints(style.getFontSize());
                font.setBold(style.isBold());
                if (style.isHighlight()) {
                    font.setColor(IndexedColors.RED.getIndex());
                }
                cellStyle.setFont(font);
            }
            return cellStyle;
        });
    }

    private void drawLine(SXSSFSheet sheet, int startColIndex, int startRowIndex,
                          int endColIndex, int endRowIndex) {
        sheet.createDrawingPatriarch();
        XSSFDrawing drawing = sheet.getDrawingPatriarch();
        XSSFClientAnchor anchor = drawing.createAnchor(0, 0, 0, 0,
                startColIndex, startRowIndex, endColIndex, endRowIndex);
        XSSFSimpleShape line = drawing.createSimpleShape(anchor);
        line.setShapeType(ShapeTypes.LINE);
        line.setLineWidth(1);
        line.setLineStyleColor(64, 64, 64);
    }

    private void setHorizontalAlignment(Cell cell, CellStyle cellStyle) {
        switch (cell.getHorizontalAlignment()) {
            case Center:
                cellStyle.setAlignment(HorizontalAlignment.CENTER);
                break;
            case Left:
                cellStyle.setAlignment(HorizontalAlignment.LEFT);
                break;
            case Right:
                cellStyle.setAlignment(HorizontalAlignment.RIGHT);
                break;
        }
    }

    private void setVerticalAlignment(Cell cell, CellStyle cellStyle) {
        switch (cell.getVerticalAlignment()) {
            case Center:
                cellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
                break;
            case Top:
                cellStyle.setVerticalAlignment(VerticalAlignment.TOP);
                break;
            case Bottom:
                cellStyle.setVerticalAlignment(VerticalAlignment.BOTTOM);
                break;
        }
    }

}
